#include <mutex>

#include "memalloc.hpp"

#ifndef ADAPTER
std::mutex MemAlloc::init_lock;
std::mutex MemAlloc::deinit_lock;
#else
bool MemAlloc::init_lock = false;
bool MemAlloc::deinit_lock = false;
#endif


MemAlloc *MemAlloc::inst;
bool MemAlloc::ma_inited = false;

MemAlloc::MemAlloc(MemAllocImpl *pimpl) {
	pimpl_ = pimpl;
}


MemAlloc::~MemAlloc() {
	delete pimpl_;
}


/*!
\throw MemAllocException
*/
MemAlloc& MemAlloc::Instance() {
        DEINIT_LOCK;
	if (ma_inited) {
                DEINIT_UNLOCK;
		return *inst;
	}
	else {
                DEINIT_UNLOCK;
		throw MemAllocException(MemAllocException::E_UNINITIALIZED, std::string("Allocator is not inited"));
	}

}


/*
\throw std::bad_alloc exception on out - of - memory
\throw std::ios_base::failure for file error
\throw MemAllocException
*/
void MemAlloc::Init(size_t mem_size, uint64_t swap_size, const std::string& swap_path) {
#ifndef ADAPTER
	if (init_lock.try_lock()) {
#else
        if(!init_lock){
                init_lock = true;
#endif
		try{
			MemAllocImpl* impl_temp = new MemAllocImpl(mem_size, swap_size,
				swap_path);
			inst = new MemAlloc(impl_temp);
		}
		catch (MemAllocException& e){
                        INIT_UNLOCK;
			throw e;
		}
		catch (...){
                        INIT_UNLOCK;
                        throw;
		}
		ma_inited = true;
	}
	else {
		throw MemAllocException(MemAllocException::E_ALREADY, "double init");
	}
}

/*
\throw MemAllocException
*/
void MemAlloc::Deinit() {
#ifndef ADAPTER
	if (deinit_lock.try_lock()) {
#else
        if(!deinit_lock){
                deinit_lock = true;
#endif
		if (ma_inited) {
			delete inst;
                        inst = 0;
			ma_inited = false;
			INIT_UNLOCK;
		}
	}
	//else : someone else is already deiniting, so pass
	DEINIT_UNLOCK;
}

